/*!
 * \file functions.cc
 * \brief Définition des fonctions.
 * \author CARBONNEL Jessie
 * \version 1.0
 * \date 11 Avril 2013
 */

#include <iostream>
#include <vector>
#include <set>

#include <time.h>
#include <math.h>

#include "Omega.h"
#include "Variable.h"
#include "Outcome.h"
#include "Preference.h"
#include "PreferenceSet.h"
#include "Constraint.h"

#include "functions.h"

using namespace std;

vector< vector<int> > scpo(PreferenceSet P, Omega O)
{
    vector< vector<int> > result; // Contiendra le résultat

    set<int> omega;
    generateIndex(O, omega);
    // Contient l'ensemble des Outcomes
    // Il s'avère qu'il est plus simple a manipuler s'il ne fait pas partie de la classe Omega


    vector<Constraint> C = generateConstraints(omega,P,O); // On genere les contraintes


    while(!omega.empty()){

        /* 	Premiere etape
            On détecte les outcomes qui ne sont présentes dans aucune partie droite des contraintes
            Et on les ajoute a E
        */

        vector<int> E;
        // On parcourt toutes les Outcomes de Omega avec un itérateur
        for(set<int>::iterator lit=omega.begin(); lit!=omega.end(); ++lit)
        {
            bool ok = true;
            int i(0);
            while( i<(int)C.size() && ok ){
                if( C[i].isInRight( *lit ) ){
                    ok = false;
                    break;
                 }
                i++;
            }
            if( ok ){
                // Si on ne l'a croisé dans aucune contrainte (partie droite)
                // Et que l'on a vérifié chaque contrainte
                // On l'ajoute a l'ensemble E
                E.push_back( *lit );

            }
        }


        // Si E est vide, on arrete la fonction
        if((int)E.size() == 0)
        {
            cout<<"INCONSISTENT"<<endl;
            return result;
        }


        /* 	Deuxième étape
            On Supprime les contraintes qui sont satisfaites :
            - Strong et CP : Ci satisfaite si la partie gauche est vide
            - Otpimist : Ci satisfaite si une outcome de E appartient a sa partie gauche
            On met a jour Omega et les contraintes restantes
        */

        for (int j=0;j<(int)E.size();j++)
        {
            // On met Omega à jour
            omega.erase(E[j]);

            for (int i((int)C.size()-1);i>=0;--i)
            {
                // On supprime les contraintes Optimist satisfaites
                if (C[i].getSemantic() == "Optimist")
                {
                    if (C[i].isInLeft(E[j])) C.erase(C.begin()+i);
                }
                // Et on met à jour les contraintes Strong et CP
                else
                {
                    C[i].deleteOutcomeInLeft(E[j]);
                    // Mise à jour des parties droites selon CP
                    if (C[i].getSemantic() == "CeterisParibus"){
                        int a = O.getCeterisParibus(E[j],P.getPreference(i),C[i].getRight());
                        C[i].deleteOutcomeInRight(a);
                    }
                }
            }
        }

        // On supprime les contraintes Strong et CP satisfaites
        for (int i((int)C.size()-1);i>=0;--i)
        {
            if (C[i].getSemantic() == "Strong" || C[i].getSemantic() == "CeterisParibus")
            {
                if( C[i].getSizeLeft() == 0 ) C.erase( C.begin() + i ) ;
            }
        }

        // On ajoute E au résultat
        result.push_back(E);
    }

    return result;

}

vector<Constraint> generateConstraints(set<int>& o,const PreferenceSet& p, Omega& O)
{
    vector<Constraint> result ;

    for(int i(0);i<(int)p.getAllPreferences().size();++i){
        result.push_back(Constraint(p.getPreference(i)));
    }
    // On parcours les Outcomes
    set<int>::iterator it (o.begin()), lend(o.end());
    for(;it!=lend;++it)
    {
        Outcome oc; O.generateOutcome(*it,oc);

        for(int i(0);i<(int)result.size();++i){

            bool mp = oc.valide(p.getPreference(i).getMostPreferred());
            bool lp = oc.valide(p.getPreference(i).getLessPreferred());

            if(p.getPreference(i).getCondition() != ""){

                bool c = oc.valide(p.getPreference(i).getCondition());

                if((mp && c) && !(lp && c)) result[i].addInLeft(*it);
                else if(!(mp && c) && (lp && c)) result[i].addInRight(*it);
            }
            else{
                if(mp && !lp) result[i].addInLeft(*it);
                else if(!mp && lp) result[i].addInRight(*it);

            }
        }
    }
    return result;

}

void generateIndex(Omega& o, std::set<int>& result){
    int taille(o.getNumberOfVariables());
    int nov[taille]; // Contient les valeurs max de chaque var
    int tab[taille]; // Compteur
    int totmax(0);
    int nMax = o.getCoefficient();

    // On place le nombre de valeurs de chaque variables dans nov[]
    for(int i(0);i<taille;++i){
        nov[i] = o.getNumberOfValues(i);
        tab[i] = 0;
        totmax = totmax + (nov[i]-1);
    }

    int tot(0);
    int it(taille-1);
    int val;
    result.insert(0);

   while(tot != totmax){
       // On incrémente le compteur tab[]
        while(tab[it] == (nov[it]-1)){
            tab[it] = 0;
            it = it -1;
        }
        tab[it] = tab[it] + 1;
        it = taille-1;
        tot = 0;
        val = 0;

        for(int i(0);i<taille;++i){
            tot = tot + tab[i];
            val += tab[i] * pow(10*nMax,taille-(i+1));

        }result.insert(val);
    }
}

